#include "SystemCall.h"
#include "Defines.h"



SystemCall::SystemCall()
{
	this->fileSys=new FileSystem() ;
}

SystemCall::SystemCall(int _diskSize, int _blockSize, int _numOfNodes)
{
	this->fileSys = new FileSystem( _diskSize,  _blockSize,  _numOfNodes);
	this->initRoot();
}

SystemCall::~SystemCall()
{
	delete(this->fileSys);
}

void SystemCall::initRoot(){
	int root =this->fileSys->createDir();
	this->fileSys->printFile(0);
	if (root != 0){
		printf("Error: root is not 0 %d\n",root);
	}
}

//*INTERFACE FUNCTIONS *//

int SystemCall::MakeFile(char * fileName , char type , bool premission){
	int inode = this->checkAndSetDir(fileName,type);
	if (inode < 0){
		printf("Error: makeFile failed path = %s\n",fileName);
		return -1;
	}
	return this->newFTE(inode,premission);
}

int SystemCall::MakeHLink(char * targetFileName , char * fileName){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer(fileName, results);
	int leng = results.size();
	int tmpInode = this->getCurrentDir(fileName ,results);
	//	printf("tmpInode = %d\n",tmpInode);
	dirList = this->fileSys->d_read(tmpInode);
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
		if (inode > -1){
			cout<<"Error:file is already existent "<<results[leng - 1]<<endl;
			return -1;
		}
	}
	inode =this->getInodeFromPath(targetFileName);
	printf("inode of hfile %d\n",inode);
	this->addInodeToDir(tmpInode, inode ,(char*)results[leng - 1].c_str() , dirList );
	this->incHlink(inode);

	return inode;
}

int SystemCall::MakeDir(char * fileName){
	return this->MakeFile(fileName , 'D' ,true);
}

int SystemCall::RmDir(char * dirName){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer(dirName, results);
	int leng = results.size();
	int directory = this->getCurrentDir(dirName ,results);
	dirList = this->fileSys->d_read(directory);
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
			if (inode < 1){
					cout<<"Error:directroy is not existent "<<results[leng - 1]<<endl;
					return -1;
				}
				else{
					dirList = this->fileSys->d_read(inode);
					if (dirList != NULL && dirList->size() > 0){
						printf("Error: the directory is not empty\n");
						return -1;
					}
					this->deleteInodeFromDir(directory,inode);
					this->deleteFile(inode);
					return inode;
				}
		}
		else{
			printf("Error:the directory of the file is empty\n");
			return -1;
		}
		return 0;

}

int SystemCall::RmFile(char * fileName){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer(fileName, results);
	int leng = results.size();
	int directory = this->getCurrentDir(fileName ,results);
//	printf("RMfile the futher dir is %d\n",directory );
//	printf("directory = %d\n",directory);
	dirList = this->fileSys->d_read(directory);
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
			if (inode < 1){
					cout<<"Error:file is not existent "<<results[leng - 1]<<endl;
					return -1;
			}
			else{
				printf("debug\n");
				this->deleteInodeFromDir(directory,inode);
				this->deleteFile(inode);//not delete if it has hard links;
				return inode;
			}
	}
	else{
		printf("Error:the directory of the file is empty %d\n",directory);
		return -1;
	}
	return 1;
}

int SystemCall::ls(char* dirName , char* buffer){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer(dirName, results);
	int leng = results.size();
	int tmpInode = this->getCurrentDir(dirName ,results);
//	printf("tmpInode = %d\n",tmpInode);
	dirList = this->fileSys->d_read(tmpInode);
//	printf("dirlist %d\n",dirList);
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
			if (inode > -1){
				dirList = this->fileSys->d_read(tmpInode);
				this->lsHelper(dirList,buffer);
			}
			else{
				printf("Error: dir not found\n");
			}
	}
	else
		printf("Error: problem with path\n");
}


int SystemCall::Open(char* fileName , bool premission){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer(fileName, results);
	int leng = results.size();
	int directory = this->getCurrentDir(fileName ,results);
	dirList = this->fileSys->d_read(directory);
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
			if (inode < 0){
				cout<<"Error:file is not existent "<<results[leng - 1]<<endl;
				return -1;
			}
			else{
				if (this->fileSys->getFileType(inode) == 'F'){
					return this->newFTE(inode,premission);
				}
				else if(this->fileSys->getFileType(inode) == 'L'){
					int softLinkSize=this->fileSys->disk->getFileSize(inode);
					char * linkFileName=(char*)malloc(softLinkSize);
					this->fileSys->f_read(inode,linkFileName,0,softLinkSize);
				//	printf("read \n[%s]\n",linkFileName);
					return this->Open(linkFileName , premission);
				}
				else {
					printf("Error:file is not file or Link");
					return -1;
				}
			}
	}
	else{//if dirlist is null
		printf("Error:the directory of the file is empty %d\n",directory);
		return -1;
	}
		return 0;
}


int SystemCall::Close(int fd){
	if (fd > -1){
		//should deal here with locks later
		map<int,FileTableEntry>::iterator it;
		it = this->fileTable.find(fd);
	//	printf("Close %d\n",it->first);
		this->fileTable.erase(it);
		return 1;
	}
	else{
		printf("Error:Close fd < 0 %d\n",fd);
		return -1;
	}
}

int SystemCall::Seek(int fd, int location){
	if (fd > -1){
		this->fileTable.find(fd)->second.location = location;
		return location;
	}
	return -1;
}


int SystemCall::Read(int fd, int nBytes , char * buffer){
	if (fd > -1){
		return this->fileSys->f_read(this->fdToInode(fd),
				buffer,this->fdToOffset(fd),nBytes);
	}
	return -1;
}

int SystemCall::Write(int fd , int nBytes , char * buffer){
	if (fd > -1){
		if (this->fdToPremission(fd)){
		return this->fileSys->f_write(this->fdToInode(fd),
					buffer,this->fdToOffset(fd),nBytes);
		}
		else{
			printf("Error:NO PREMISSION\n");
			return -1;
		}
	}
	return -1;
}


int SystemCall::moveFile(char* parendDir , char * new_dest){
	this->MakeHLink( parendDir ,  new_dest);
	return this->RmFile(parendDir);
}

bool SystemCall::isDir(char * address){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer(address, results);
	int leng = results.size();
	int directory = this->getCurrentDir(address ,results);
	dirList = this->fileSys->d_read(directory);
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
			if (inode < 1){
				cout<<"Error:directroy is not existent "<<results[leng - 1]<<endl;
				return -1;
			}
			else{
				return this->fileSys->getFileType(inode) == 'D';
			}
	}
	else{
		printf("Error:the directory of the file is empty\n");
		return -1;
	}
	return 0;
}

//*HELPING FUNCTIONS*//

int SystemCall::createSoftLink(char* shoutcutPlace,char* fileName){

	int fd =this->MakeFile(shoutcutPlace,'L',true);
	if (fd > 0){
		int inode = this->fileTable.find(fd)->second.inode;
		this->Close(fd);
		int nBytes=strlen(fileName);
	//	printf("write shourtcut inode %d,file len %d path \n[%s]\n",inode,nBytes,fileName);
		this->fileSys->f_write(inode,fileName,0,nBytes);
		return this->Open(shoutcutPlace,fileName);
	}
	return -1;
}

int SystemCall::fileTableSize(){
	return this->fileTable.size();
}

int SystemCall::checkAndSetDir(string path , char flag){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer((char*)path.c_str(), results);
	int leng = results.size();
	int tmpInode = this->getCurrentDir(path ,results);
//	printf("tmpInode = %d\n",tmpInode);
	dirList = this->fileSys->d_read(tmpInode);
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
		if (inode > -1){
				cout<<"Error:file is already existent "<<results[leng - 1]<<endl;
				return -1;
			}
	}
	inode = this->fileSys->createFile(flag);
	this->addInodeToDir(tmpInode, inode ,(char*)results[leng - 1].c_str() , dirList );
	return inode;
}

void SystemCall::addInodeToDir(int dir,int inode,char* fileName ,list<pair<int , char*> >* dirList){

	char * tmp =(char*)malloc(13);

	if (dirList == NULL){
		//	printf("NULL\n");
			dirList=new list<pair<int,char*> >();
		}

//	printf("filename is %s\n",fileName);
	pair<int , char*> tmpPair;

	tmpPair.first = inode;
	this->fileSys->strCopy( tmp, fileName ,strlen(fileName));
	this->fileSys->strCopy( tmp+strlen(fileName), "\0" ,1);
//	printf("tmp is %s\n",tmp);
	this->fileSys->makeValidName(tmp);
	this->fileSys->strCopy( tmp+13, "\0" ,1);
//	printf("second = %s\n",tmp);
	tmpPair.second =tmp;


	dirList->push_back(tmpPair);
	this->fileSys->d_write(dir,dirList);
}

void SystemCall::pathTokenizer(char * path, vector<string> &results){
	unsigned int cutAt;
	string str=string(path);
	string delim=string("/");
	while( (cutAt = str.find_first_of(delim)) != str.npos )
	{
		if(cutAt > 0){
			results.push_back(str.substr(0,cutAt));
		}
		str = str.substr(cutAt+1);
	}
	if(str.length() > 0){
		results.push_back(str);
	}
}


int SystemCall::deleteInodeFromDir(int dir , int inode){

	list<pair<int,char*> >* dirList;
	dirList = this->fileSys->d_read(dir);
	if (dirList == NULL){
		return -1;
	}
	list<pair<int,char*> >::iterator it;
	for ( it = dirList->begin(); it != dirList->end(); it++){
		if (it->first == inode){
		//	printf("dirList erase [%s]\n",it->second);
			dirList->erase(it);
			break;
		}
	}
	this->fileSys->d_write(dir , dirList);
	return  0;

}


int SystemCall::getCurrentDir(string path , vector<string> results){
	list<pair<int,char*> >* dirList;
	int i = 0;
	string tmpDir = results[0];
	if (tmpDir.compare("root" ) != 0){
		cout<<"Error:the first librery is not root"<<tmpDir<<endl;
		return -1;
	}
	if (results.size() == 1) return 0;
	int tmpInode = 0;//root
	for (i = 1 ; i < results.size() - 1 ; i++){
		dirList = this->fileSys->d_read(tmpInode);
		if (dirList == NULL){
			cout<<"Error:path is not existent1 "<<results[i]<<endl;
			return -1;
		}
		tmpInode = this->fileSys->getInodeNumber((char*)results.at(i).c_str() ,
				dirList);
		if (tmpInode < 1) {
			cout<<"Error:path is not existent2 "<<results[i]<<endl;
			return -1;
		}
	}
	return tmpInode;
}

int SystemCall::getInodeFromPath(char* path){
	vector<string> results;
	list<pair<int,char*> >* dirList;
	int i = 0;
	this->pathTokenizer(path , results);
	string tmpDir = results[0];
	if (tmpDir.compare("root" ) != 0){
		cout<<"Error:the first librery is not root"<<tmpDir<<endl;
		return -1;
		}
		if (results.size() == 1) return 0;
		int tmpInode = 0;//root
		for (i = 1 ; i < results.size(); i++){
			dirList = this->fileSys->d_read(tmpInode);
			if (dirList == NULL){
				cout<<"Error:path is not existent1 "<<results[i]<<endl;
				return -1;
			}
			tmpInode = this->fileSys->getInodeNumber((char*)results.at(i).c_str() ,
					dirList);
			if (tmpInode < 1) {
				cout<<"Error:path is not existent2 "<<results[i]<<endl;
				return -1;
			}
		}
		return tmpInode;
}

int SystemCall::newFTE(int inode , bool premission){
	int size = this->fileTable.size();
	if (size < 120){
		FileTableEntry fte= FileTableEntry(inode , 0 , premission);
		this->fileTable.insert(pair<int,FileTableEntry>(size,fte));
		return size;
	}
	return -1;
}


int SystemCall::deleteFile(int inode){
	char ans = this ->fileSys->disk->getInodeHlink(inode);
	char* tmp2 = &ans;
	int hlink = atoi(tmp2);
	if (hlink > 0){
		char * tmp1 = this->fileSys->itoa(hlink - 1);
		this->fileSys->disk->setInodeHlink(inode,tmp1);
		return hlink - 1;
	}
	return this->fileSys->disk->freeInode(inode);
}

void SystemCall::incHlink(int inode){

	char a =this->fileSys->disk->getInodeHlink(inode);
	a = a + 1;
	char * tmp =(char *)malloc(1);
	tmp[0] =a;
	this->fileSys->disk->setInodeHlink(inode,tmp);
	free(tmp);
}


int SystemCall::lsHelper(list<pair<int,char*> >* dirList , char * buffer){
	if (dirList !=NULL ){
			list<pair<int,char*> >::const_iterator it;
			int i=0;
			int j=0;
			for ( it = dirList->begin();	it != dirList->end(); it++){
			//	printf("%s\n",it->second);
				j=this->cleenName(it->second);
				if (j!=-1){
					this->fileSys->strCopy(buffer+i,it->second,j-1);
					this->fileSys->strCopy(buffer+i+j-1,"\n\0",2);
					i=i+j;

				}
				else{
					printf("Debug:problem with copy dir list\n");
					return -1;
				}
			}
		}
	return 1;//just not warning
}

int SystemCall::fdToInode(int fd){
	return this->fileTable.find(fd)->second.inode;
}

int SystemCall::fdToOffset(int fd){
	return this->fileTable.find(fd)->second.location;
}

bool SystemCall::fdToPremission(int fd){
	return this->fileTable.find(fd)->second.writePremission;
}

int SystemCall::cleenName(char* name){
	if (this->fileSys->memChar(name,'/',13) < 1 )
		return -1;
	int i=0;
	while (i < 13 && name[i] !='/'  ){
		i++;
	}
	i++;
	this->fileSys->strCopy(name+i,"\0",1);
	return i;
}


void SystemCall::printDir(char* dirName){
	list<pair<int,char*> >* dirList;
	int inode;
	vector<string> results;
	this->pathTokenizer(dirName, results);

	int leng = results.size();

	int tmpInode = this->getCurrentDir(dirName ,results);
	dirList = this->fileSys->d_read(tmpInode);
//	printf("\nprinting futher dir list\n");
//	this->printList(dirList);
//	printf("\n");
	if (dirList != NULL){
		inode = this->fileSys->getInodeNumber( (char*)results[leng - 1].c_str(),dirList);
	//	printf("found inode %d\n",inode);
			if (inode > -1){
				printf("\nprinting dir list\n");
				dirList = this->fileSys->d_read(tmpInode);
			//	this->printList(dirList);
				char * buffer =(char*)malloc(14*(dirList->size()));
				this->lsHelper(dirList,buffer);
				printf("~~~~~~~~~~~~~~~~~~~~\n%s\n",buffer);
			}
			else{
				printf("Error: dir not found\n");
			}
	}
	else{
		printf("Error: problem with path\n");
	}
}

void SystemCall::printList(list<pair<int,char*> >* dirList){
	if (dirList !=NULL ){
		list<pair<int,char*> >::const_iterator it;
		for ( it = dirList->begin();	it != dirList->end(); it++){
			printf("%s\n",it->second);
		}
	}
}






